import socket
import os
import struct
import datetime

PORT = 4321
MAX_FILENAME = 256
BUF_SIZE = 4096

def recv_all(conn, length):
    data = b''
    while len(data) < length:
        try:
            chunk = conn.recv(length - len(data))
        except Exception as e:
            print(f"接收异常: {e}")
            return None
        if chunk is None:
            return None
        if chunk == b'':
            # 只有真正断开才返回None
            return None
        data += chunk
    return data

def main_photo_server():
    if not os.path.exists('photos'):
        os.makedirs('photos')

    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind(('0.0.0.0', PORT))
    sock.listen(1)
    print(f"等待连接在端口{PORT}...")

    try:
        while True:
            conn, addr = sock.accept()
            print(f"连接来自: {addr[0]}")
            try:
                while True:
                    # 1. 接收4字节（文件名长度或心跳包）
                    header = recv_all(conn, 4)
                    if header is None:
                        print("客户端断开连接，关闭连接")
                        break
                    if len(header) != 4:
                        print("收到无效头，忽略")
                        continue
                    # 检查心跳包
                    if header == b'PING':
                        try:
                            conn.sendall(b'PONG')
                        except Exception as e:
                            print(f"回复PONG失败: {e}")
                            break
                        print("收到心跳包，已回复PONG")
                        continue
                    # 文件名长度
                    filename_size = struct.unpack('<I', header)[0]
                    print(f"接收文件名大小: {filename_size}字节")
                    if filename_size <= 0 or filename_size > 255:
                        print(f"Invalid filename size: {filename_size}")
                        continue
                    # 文件名
                    filename_bytes = recv_all(conn, filename_size)
                    if filename_bytes is None or len(filename_bytes) != filename_size:
                        print("文件名接收失败，忽略")
                        continue
                    filename = filename_bytes.decode('utf-8', errors='ignore').strip()
                    if len(filename) < 4 or not filename.endswith('.jpg'):
                        print("Invalid file extension")
                        continue
                    # 生成以时间为名的文件名
                    now = datetime.datetime.now()
                    time_filename = now.strftime("photos/%Y%m%d_%H%M%S_%f")[:-3] + ".jpg"
                    print(f"保存文件名: {time_filename}")
                    # 文件大小
                    size_buf = recv_all(conn, 4)
                    if size_buf is None or len(size_buf) != 4:
                        print("Invalid file size header，忽略")
                        continue
                    file_size = struct.unpack('<I', size_buf)[0]
                    print(f"接收文件大小: {file_size}字节")
                    if file_size <= 0:
                        print(f"Invalid file size: {file_size}")
                        continue
                    # 文件内容
                    received = 0
                    os.makedirs('photos', exist_ok=True)  # 确保目录存在
                    with open(time_filename, 'wb') as f:
                        while received < file_size:
                            to_read = min(BUF_SIZE, file_size - received)
                            chunk = conn.recv(to_read)
                            if not chunk:
                                print("文件内容接收中断")
                                break
                            f.write(chunk)
                            received += len(chunk)
                            print(f"已接收: {received}/{file_size}字节", end='\r')
                        f.flush()
                        os.fsync(f.fileno())
                    print(f"\n文件保存成功: {time_filename}")
            finally:
                conn.close()
                print("连接关闭")
    except KeyboardInterrupt:
        print("\n服务器关闭")
    finally:
        sock.close()

# 如需运行图片接收服务器，请调用 main_photo_server()

import tkinter as tk
from tkinter import ttk, messagebox
import serial
import serial.tools.list_ports
import threading

class SerialSender:
    # ===== UDP服务器相关 =====
    def start_udp_server(self):
        import socket
        import threading
        import tkinter as tk
        from tkinter import messagebox
        HOST = '0.0.0.0'
        PORT = 12345
        # 若窗口已存在且未销毁，先安全销毁
        if hasattr(self, '_udp_win') and self._udp_win is not None:
            try:
                if self._udp_win.winfo_exists():
                    self._udp_win.destroy()
            except Exception:
                pass
            self._udp_win = None
        if hasattr(self, '_udp_running') and self._udp_running:
            messagebox.showinfo("UDP服务器", "UDP服务器已在运行！请点击结束GSENSOR数据接收!")
            return
        self._udp_running = True
        self._udp_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self._udp_sock.bind((HOST, PORT))
        self._udp_win = tk.Toplevel(self.master)
        self._udp_win.title("科奈信GSENSOR数据")
        self._udp_win.geometry("1200x600")
        self._udp_label = tk.Label(self._udp_win, text=f"UDP服务器已启动，监听端口 {PORT}", font=("Arial", 12))
        self._udp_label.pack(pady=20)
        self._udp_msg_text = tk.Text(self._udp_win, font=("Arial", 12), height=35, width=90, state='disabled')
        self._udp_msg_text.pack(pady=10, fill='both', expand=True)
        def udp_thread():
            try:
                while self._udp_running:
                    self._udp_sock.settimeout(0.5)
                    try:
                        data, addr = self._udp_sock.recvfrom(4096)
                        msg = f"收到来自{addr}的数据: {data.decode(errors='ignore')}\n"
                        # 线程安全追加到Text控件
                        if hasattr(self, '_udp_msg_text') and self._udp_msg_text is not None:
                            try:
                                if self._udp_msg_text.winfo_exists():
                                    def append_msg():
                                        # 回调内再次判断控件是否存在
                                        if self._udp_msg_text is not None and self._udp_msg_text.winfo_exists():
                                            self._udp_msg_text.config(state='normal')
                                            self._udp_msg_text.insert('end', msg)
                                            self._udp_msg_text.see('end')
                                            self._udp_msg_text.config(state='disabled')
                                    self._udp_msg_text.after(0, append_msg)
                            except Exception:
                                pass
                    except socket.timeout:
                        continue
            except Exception as e:
                # 线程安全追加异常到Text控件
                if hasattr(self, '_udp_msg_text') and self._udp_msg_text is not None:
                    try:
                        if self._udp_msg_text.winfo_exists():
                            def append_err():
                                if self._udp_msg_text is not None and self._udp_msg_text.winfo_exists():
                                    self._udp_msg_text.config(state='normal')
                                    self._udp_msg_text.insert('end', f"接收异常: {e}\n")
                                    self._udp_msg_text.see('end')
                                    self._udp_msg_text.config(state='disabled')
                            self._udp_msg_text.after(0, append_err)
                    except Exception:
                        pass
            finally:
                self._udp_sock.close()
                self._udp_running = False
                # 修复窗口已销毁时再次destroy导致的NoneType异常
                if hasattr(self, '_udp_win') and self._udp_win is not None:
                    try:
                        if self._udp_win.winfo_exists():
                            self._udp_win.destroy()
                    except Exception:
                        pass
                self._udp_win = None
        self._udp_thread = threading.Thread(target=udp_thread, daemon=True)
        self._udp_thread.start()

    def stop_udp_server(self):
        import tkinter as tk
        if hasattr(self, '_udp_running') and self._udp_running:
            self._udp_running = False
            # 关闭窗口（线程会自动关闭socket和窗口）
            if hasattr(self, '_udp_win') and self._udp_win is not None and self._udp_win.winfo_exists():
                try:
                    self._udp_win.destroy()
                except Exception:
                    pass
        else:
            tk.messagebox.showinfo("UDP服务器", "UDP服务器未在运行！")
    # ===== OTA HTTP服务器相关 =====
    def start_ota_http_server(self):
        import socket
        import threading
        import os
        import tkinter as tk
        from tkinter import messagebox

        HOST = ''
        PORT = 8080
        BUF_SIZE = 4096
        OTA_FILE_PATH = 'bin/ota.bin'

        def send_ota_file(conn, filename):
            print(f"[INFO] 客户端请求OTA文件: {filename}")
            # 界面显示已连接
            if hasattr(send_ota_file, 'info_label'):
                send_ota_file.info_label.config(text="已连接，正在发送文件...")
            if not os.path.exists(filename):
                not_found = "HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n"
                conn.sendall(not_found.encode())
                # 弹窗提醒
                messagebox.showerror("文件未找到", f"未找到OTA文件: {filename}")
                return
            filesize = os.path.getsize(filename)
            header = (
                "HTTP/1.1 200 OK\r\n"
                "Content-Type: application/octet-stream\r\n"
                f"Content-Length: {filesize}\r\n"
                "Content-Disposition: attachment; filename=\"ota.bin\"\r\n"
                "Connection: close\r\n\r\n"
            )
            conn.sendall(header.encode())
            total = 0
            with open(filename, 'rb') as f:
                while True:
                    chunk = f.read(BUF_SIZE)
                    if not chunk:
                        break
                    conn.sendall(chunk)
                    total += len(chunk)
            print(f"[INFO] 已发送OTA文件，总字节数: {total}")
            # 界面显示已发送完成
            if hasattr(send_ota_file, 'info_label'):
                send_ota_file.info_label.config(text="已发送完成，2秒后自动关闭窗口...")
            # 等待2秒后关闭OTA窗口，不退出主程序
            import time
            time.sleep(2)
            if hasattr(send_ota_file, 'ota_win'):
                try:
                    send_ota_file.ota_win.destroy()
                except Exception:
                    pass

        def handle_client(conn, addr):
            print(f"[INFO] 客户端已连接: {addr}")
            try:
                buffer = conn.recv(1024).decode(errors='ignore')
                print(f"[INFO] 收到请求:\n{buffer}")
                if "GET /bin/ota.bin" in buffer or "GET //bin/ota.bin" in buffer:
                    send_ota_file(conn, OTA_FILE_PATH)
                else:
                    print("[WARN] 未知请求，返回404")
                    not_found = "HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n"
                    conn.sendall(not_found.encode())
            except Exception as e:
                print(f"[ERROR] 处理客户端异常: {e}")
            finally:
                conn.close()
                print("[INFO] 客户端连接已关闭")

        def server_thread_func():
            send_ota_file.info_label = self.ota_info_label
            server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            server_sock.bind((HOST, PORT))
            server_sock.listen(5)
            print(f"OTA HTTP服务器已启动，端口{PORT}")
            while True:
                self.ota_info_label.config(text="等待客户端连接...")
                conn, addr = server_sock.accept()
                self.ota_info_label.config(text=f"客户端已连接: {addr}")
                threading.Thread(target=handle_client, args=(conn, addr), daemon=True).start()

        # 弹出一个简单窗口显示状态
        ota_win = tk.Toplevel(self.master)
        ota_win.title("科奈信8735b升级程序")
        ota_win.geometry("400x200")
        status_label = tk.Label(ota_win, text=f"服务器端口: {PORT}", font=("Arial", 14))
        status_label.pack(pady=20)
        self.ota_info_label = tk.Label(ota_win, text="等待客户端连接...", font=("Arial", 12))
        self.ota_info_label.pack(pady=10)
        # 记录窗口对象，便于关闭
        def set_ota_win():
            send_ota_file.ota_win = ota_win
        ota_win.after(0, set_ota_win)

        # 启动时判断文件是否存在
        if not os.path.exists(OTA_FILE_PATH):
            messagebox.showerror("文件未找到", f"未找到OTA文件: {OTA_FILE_PATH}")
            ota_win.destroy()
            return

        threading.Thread(target=server_thread_func, daemon=True).start()
    def parse_mode(self, parsed, raw):
        # 通用模式返回
        return "设置成功" if parsed else "失败"

    def __init__(self, master):
        self.master = master
        master.title("科奈信调试工具V0.05")

        # 串口选择
        self.port_label = tk.Label(master, text="选择串口：")
        self.port_label.grid(row=0, column=0, padx=5, pady=5)
        self.port_combo = ttk.Combobox(master, state="readonly", width=30)
        self.port_combo.grid(row=0, column=1, padx=5, pady=5)
        self.refresh_ports()

        self.refresh_button = tk.Button(master, text="刷新串口", command=self.refresh_ports)
        self.refresh_button.grid(row=0, column=2, padx=5, pady=5)

        # 十六进制输入
        self.hex_label = tk.Label(master, text="输入十六进制：")
        self.hex_label.grid(row=1, column=0, padx=5, pady=5)
        self.hex_entry = tk.Entry(master, width=40)
        self.hex_entry.grid(row=1, column=1, padx=5, pady=5, columnspan=2)

        # 发送按钮
        self.send_button = tk.Button(master, text="发送", command=self.send_hex)
        self.send_button.grid(row=1, column=2, padx=5, pady=10)

        # 十六进制返回显示框
        self.hex_recv_label = tk.Label(master, text="返回十六进制：")
        self.hex_recv_label.grid(row=1, column=3, padx=5, pady=5)
        self.hex_recv_entry = tk.Entry(master, width=80, state='readonly')
        self.hex_recv_entry.grid(row=1, column=4, padx=5, pady=5, columnspan=3)

        # 开关机按钮
        # 安卓/iOS设备按钮
        self.android_button = tk.Button(master, text="安卓设备(85E2)", command=self.send_android)
        self.android_button.grid(row=3, column=0, padx=5, pady=10)
        self.android_result = tk.Entry(master, width=15, state='readonly')
        self.android_result.grid(row=3, column=1, padx=5, pady=10)

        self.ios_button = tk.Button(master, text="iOS设备(85E2)", command=self.send_ios)
        self.ios_button.grid(row=3, column=2, padx=5, pady=10)
        self.ios_result = tk.Entry(master, width=15, state='readonly')
        self.ios_result.grid(row=3, column=3, padx=5, pady=10)

        self.poweron_button = tk.Button(master, text="WIFI开机(8587)", command=self.send_poweron)
        self.poweron_button.grid(row=3, column=4, padx=5, pady=10)
        self.poweron_result = tk.Entry(master, width=15, state='readonly')
        self.poweron_result.grid(row=3, column=5, padx=5, pady=10)

        self.poweroff_button = tk.Button(master, text="WIFI关机(8588)", command=self.send_poweroff)
        self.poweroff_button.grid(row=3, column=6, padx=5, pady=10)
        self.poweroff_result = tk.Entry(master, width=15, state='readonly')
        self.poweroff_result.grid(row=3, column=7, padx=5, pady=10)

        self.get_wifi_ip_button = tk.Button(master, text="获取wifi IP(8585)", command=self.send_get_wifi_ip)
        self.get_wifi_ip_button.grid(row=4, column=0, padx=5, pady=10)
        self.get_wifi_ip_result = tk.Entry(master, width=20, state='readonly')
        self.get_wifi_ip_result.grid(row=4, column=1, padx=5, pady=10)

        self.get_wifi_version_button = tk.Button(master, text="获取wifi版本号(8580)", command=self.send_get_wifi_version)
        self.get_wifi_version_button.grid(row=4, column=2, padx=5, pady=10)
        self.get_wifi_version_result = tk.Entry(master, width=15, state='readonly')
        self.get_wifi_version_result.grid(row=4, column=3, padx=5, pady=10)

        self.set_wifi_ota_button = tk.Button(master, text="设置wifi OTA(8586)", command=self.send_set_wifi_ota)
        self.set_wifi_ota_button.grid(row=4, column=4, padx=5, pady=10)
        self.set_wifi_ota_result = tk.Entry(master, width=15, state='readonly')
        self.set_wifi_ota_result.grid(row=4, column=5, padx=5, pady=10)

        self.set_wifi_emmc_format = tk.Button(master, text="格式化EMMC(85A0)", command=self.send_set_wifi_emmc_format)
        self.set_wifi_emmc_format.grid(row=4, column=6, padx=5, pady=10)
        self.set_wifi_emmc_format = tk.Entry(master, width=15, state='readonly')
        self.set_wifi_emmc_format.grid(row=4, column=7, padx=5, pady=10)

        # row=5 增加两个输入框和按钮
        self.text_hex_label1 = tk.Label(self.master, text="SSID输入(8582)：")
        self.text_hex_label1.grid(row=5, column=0, padx=5, pady=10)
        self.text_hex_entry1 = tk.Entry(self.master, width=20)
        self.text_hex_entry1.grid(row=5, column=1, padx=5, pady=10)
        self.text_hex_btn1 = tk.Button(self.master, text="设置账号", command=lambda: self.send_text_hex(self.text_hex_entry1, None, b'\x82\x85'))
        self.text_hex_btn1.grid(row=5, column=2, padx=5, pady=10)
        self.text_hex_result1 = tk.Entry(self.master, width=10, state='readonly')
        self.text_hex_result1.grid(row=5, column=3, padx=5, pady=10)

        self.text_hex_label2 = tk.Label(self.master, text="PASSWD输入(8583)：")
        self.text_hex_label2.grid(row=5, column=4, padx=5, pady=10)
        self.text_hex_entry2 = tk.Entry(self.master, width=20)
        self.text_hex_entry2.grid(row=5, column=5, padx=5, pady=10)
        self.text_hex_btn2 = tk.Button(self.master, text="设置密码", command=lambda: self.send_text_hex(self.text_hex_entry2, self.text_hex_result2, b'\x83\x85'))
        self.text_hex_btn2.grid(row=5, column=6, padx=5, pady=10)
        self.text_hex_result2 = tk.Entry(self.master, width=10, state='readonly')
        self.text_hex_result2.grid(row=5, column=7, padx=5, pady=10)

        self.set_wifi_mode = tk.Button(master, text="WIFI模式(8590)", command=self.send_set_wifi_mode)
        self.set_wifi_mode.grid(row=6, column=0, padx=5, pady=10)
        self.set_wifi_mode = tk.Entry(master, width=15, state='readonly')
        self.set_wifi_mode.grid(row=6, column=1, padx=5, pady=10)

        self.set_wifi_rtsp_start = tk.Button(master, text="开启RTSP(8581)", command=self.send_set_wifi_rtsp_start)
        self.set_wifi_rtsp_start.grid(row=6, column=2, padx=5, pady=10)
        self.set_wifi_rtsp_start = tk.Entry(master, width=15, state='readonly')
        self.set_wifi_rtsp_start.grid(row=6, column=3, padx=5, pady=10)

        self.set_wifi_rtsp_stop = tk.Button(master, text="停止RTSP(8589)", command=self.send_set_wifi_rtsp_stop)
        self.set_wifi_rtsp_stop.grid(row=6, column=4, padx=5, pady=10)
        self.set_wifi_rtsp_stop = tk.Entry(master, width=15, state='readonly')
        self.set_wifi_rtsp_stop.grid(row=6, column=5, padx=5, pady=10)

        self.get_wifi_gw = tk.Button(master, text="WIFI网关(8591)", command=self.send_get_wifi_gw)
        self.get_wifi_gw.grid(row=6, column=6, padx=5, pady=10)
        self.get_wifi_gw = tk.Entry(master, width=15, state='readonly')
        self.get_wifi_gw.grid(row=6, column=7, padx=5, pady=10)

        # IP地址设置输入框和按钮（row=7）
        self.set_ip_label = tk.Label(master, text="设置IP地址(8597)：")
        self.set_ip_label.grid(row=7, column=0, padx=5, pady=10)
        self.set_ip_entry = tk.Entry(master, width=20)
        self.set_ip_entry.grid(row=7, column=1, padx=5, pady=10)
        self.set_ip_btn = tk.Button(master, text="设置IP", command=self.send_set_ip)
        self.set_ip_btn.grid(row=7, column=2, padx=5, pady=10)
        self.set_ip_result = tk.Entry(master, width=15, state='readonly')
        self.set_ip_result.grid(row=7, column=3, padx=5, pady=10)

        self.set_wifi_sta_ota = tk.Button(master, text="STA OTA(85B5)", command=self.send_set_wifi_sta_ota)
        self.set_wifi_sta_ota.grid(row=7, column=4, padx=5, pady=10)
        self.set_wifi_sta_ota = tk.Entry(master, width=15, state='readonly')
        self.set_wifi_sta_ota.grid(row=7, column=5, padx=5, pady=10)


        self.travel_mode_btn = tk.Button(self.master, text="出行模式拍照(85B0)", command=self.on_travel_mode)
        self.travel_mode_btn.grid(row=8, column=0, padx=5, pady=10)
        self.travel_mode_result = tk.Entry(self.master, width=15, state='readonly')
        self.travel_mode_result.grid(row=8, column=1, padx=5, pady=10)

        self.qa_mode_btn = tk.Button(self.master, text="问答模式拍照(85B1)", command=self.on_qa_mode)
        self.qa_mode_btn.grid(row=8, column=2, padx=5, pady=10)
        self.qa_mode_result = tk.Entry(self.master, width=15, state='readonly')
        self.qa_mode_result.grid(row=8, column=3, padx=5, pady=10)

        self.read_mode_btn = tk.Button(self.master, text="阅读模式拍照(85B2)", command=self.on_read_mode)
        self.read_mode_btn.grid(row=8, column=4, padx=5, pady=10)
        self.read_mode_result = tk.Entry(self.master, width=15, state='readonly')
        self.read_mode_result.grid(row=8, column=5, padx=5, pady=10)

        self.full_pixel_mode_btn = tk.Button(self.master, text="全像素模式拍照(85B4)", command=self.on_full_pixel_mode)
        self.full_pixel_mode_btn.grid(row=8, column=6, padx=5, pady=10)
        self.full_pixel_mode_result = tk.Entry(self.master, width=15, state='readonly')
        self.full_pixel_mode_result.grid(row=8, column=7, padx=5, pady=10)

        # row=9：高清模式拍照、查找模式拍照，gsensor相关按钮顺序后移
        self.hd_mode_btn = tk.Button(self.master, text="高清模式拍照(85AF)", command=self.on_hd_mode)
        self.hd_mode_btn.grid(row=9, column=0, padx=5, pady=10)
        self.hd_mode_result = tk.Entry(self.master, width=15, state='readonly')
        self.hd_mode_result.grid(row=9, column=1, padx=5, pady=10)

        self.search_mode_btn = tk.Button(self.master, text="查找模式拍照(85B3)", command=self.on_search_mode)
        self.search_mode_btn.grid(row=9, column=2, padx=5, pady=10)
        self.search_mode_result = tk.Entry(self.master, width=15, state='readonly')
        self.search_mode_result.grid(row=9, column=3, padx=5, pady=10)

        self.udp_server_start_btn = tk.Button(self.master, text="开启gsensor", command=self.start_gsensor)
        self.udp_server_start_btn.grid(row=9, column=4, padx=5, pady=10)

        self.udp_server_stop_btn = tk.Button(self.master, text="结束gsensor", command=self.stop_gsensor)
        self.udp_server_stop_btn.grid(row=9, column=5, padx=5, pady=10)

    def on_travel_mode(self):
        self._clear_entry(self.travel_mode_result)
        self.send_custom_hex('AA000300B08501', self.travel_mode_result, self.parse_mode)

    def on_qa_mode(self):
        self._clear_entry(self.qa_mode_result)
        self.send_custom_hex('AA000300B18501', self.qa_mode_result, self.parse_mode)

    def on_read_mode(self):
        self._clear_entry(self.read_mode_result)
        self.send_custom_hex('AA000300B28501', self.read_mode_result, self.parse_mode)


    def on_full_pixel_mode(self):
        self._clear_entry(self.full_pixel_mode_result)
        self.send_custom_hex('AA000300B48501', self.full_pixel_mode_result, self.parse_mode)

    def on_hd_mode(self):
        self._clear_entry(self.hd_mode_result)
        self.send_custom_hex('AA000300AF8501', self.hd_mode_result, self.parse_mode)

    def on_search_mode(self):
        self._clear_entry(self.search_mode_result)
        self.send_custom_hex('AA000300B38501', self.search_mode_result, self.parse_mode)

    def start_gsensor(self):
        # 只启动UDP服务器和发送指令，不清空按钮内容
        self.start_udp_server()
        # 如需显示结果，建议绑定到一个Entry控件
        self.send_custom_hex('AA000300938501', None, self.parse_mode)

    def stop_gsensor(self):
        # 只发送指令和关闭UDP服务器，不清空按钮内容
        self.send_custom_hex('AA000300948501', None, self.parse_mode)
        self.stop_udp_server()

    def send_set_wifi_sta_ota(self):
        import threading
        import time
        def delayed_send():
            time.sleep(1)
            self._clear_entry(self.set_wifi_sta_ota)
            self.send_custom_hex('AA000300B58501', self.set_wifi_sta_ota, self.parse_mode)
        self.start_ota_http_server()
        threading.Thread(target=delayed_send, daemon=True).start()

    def send_set_ip(self):
        ip_text = self.set_ip_entry.get().strip()
        
        # 校验IP格式
        try:
            ip_parts = [int(x) for x in ip_text.split('.')]
            if len(ip_parts) != 4 or not all(0 <= x <= 255 for x in ip_parts):
                raise ValueError
        except Exception:
            self.set_ip_result.config(state='normal')
            self.set_ip_result.delete(0, tk.END)
            self.set_ip_result.insert(0, "IP格式错误")
            self.set_ip_result.config(state='readonly')
            return
        # 构造AA 00 06 00 97 85 ip1 ip2 ip3 ip4
        packet = bytearray([0xAA, 0x00, 0x06, 0x00, 0x97, 0x85])
        packet += bytes(ip_parts)
        hex_str = packet.hex(' ').upper()
        # 显示hex结果
        self.set_ip_result.config(state='normal')
        self.set_ip_result.delete(0, tk.END)
        self.set_ip_result.insert(0, hex_str)
        self.set_ip_result.config(state='readonly')
        # 发送串口
        port_info = self.port_combo.get()
        if not port_info:
            messagebox.showerror("错误", "请选择串口！")
            return
        port_name = port_info.split(" - ")[0]
        def worker():
            try:
                ser = serial.Serial(port_name, 9600, timeout=0.2)
                ser.write(packet)
                recv = ser.read(64)
                # 简单判断返回
                result = "设置成功" if recv else "无返回"
                self.safe_update_entry(self.set_ip_result, result)
                ser.close()
                # 设置成功后自动启动图片接收服务器（仅首次启动）
                if result == "设置成功":
                    import threading
                    def start_photo_server():
                        try:
                            main_photo_server()
                        except Exception as e:
                            print(f"[ERROR] 图片服务器启动失败: {e}")
                    threading.Thread(target=start_photo_server, daemon=True).start()
            except Exception as e:
                self.safe_update_entry(self.set_ip_result, f"串口错误: {e}")
        threading.Thread(target=worker, daemon=True).start()

    def send_text_hex(self, entry, result_entry=None, head_bytes=b'\x82\x85'):
        text = entry.get()
        # 支持汉字和字母，utf-8编码
        data_bytes = text.encode('utf-8')
        length = len(data_bytes)+2
        # 构造AA 00 len 00 head_bytes + 内容
        header = bytearray([0xAA, 0x00, length, 0x00]) + head_bytes
        packet = header + data_bytes
        hex_str = packet.hex(' ').upper()
        # 显示hex结果
        if result_entry is None:
            self.text_hex_result1.config(state='normal')
            self.text_hex_result1.delete(0, tk.END)
            self.text_hex_result1.insert(0, hex_str)
            self.text_hex_result1.config(state='readonly')
        else:
            result_entry.config(state='normal')
            result_entry.delete(0, tk.END)
            result_entry.insert(0, hex_str)
            result_entry.config(state='readonly')
        # 发送串口
        port_info = self.port_combo.get()
        if not port_info:
            messagebox.showerror("错误", "请选择串口！")
            return
        port_name = port_info.split(" - ")[0]
        threading.Thread(target=self.serial_worker, args=(port_name, bytes(packet), self.text_hex_result1 if result_entry is None else result_entry), daemon=True).start()

    def send_android(self):
        self._clear_entry(self.android_result)
        self.send_custom_hex('AA000300E28501', self.android_result, self.parse_device_type)

    def send_ios(self):
        self._clear_entry(self.ios_result)
        self.send_custom_hex('AA000300E28503', self.ios_result, self.parse_device_type)

    def parse_device_type(self, parsed, raw):
        # 解析data为1或3
        if len(raw) >= 7 and raw[0] == 0xAA and raw[2:4] == b'\x03\x00' and raw[4:6] == b'\xE2\x85':
            data = raw[6]
            if data == 1:
                return "安卓设备"
            elif data == 3:
                return "iOS设备"
        return "未知设备"

    def send_poweron(self):
        self._clear_entry(self.poweron_result)
        self.send_custom_hex('AA000300888501', self.poweron_result, self.parse_poweron)

    def send_poweroff(self):
        self.clear_all_results()
        self.send_custom_hex('AA000300878501', self.poweroff_result, self.parse_poweroff)

    def clear_all_results(self):
    def send_set_ip(self):
        ip_text = self.set_ip_entry.get().strip()
        
        # 校验IP格式
        try:
            ip_parts = [int(x) for x in ip_text.split('.')]
            if len(ip_parts) != 4 or not all(0 <= x <= 255 for x in ip_parts):
                raise ValueError
        except Exception:
            self.set_ip_result.config(state='normal')
            self.set_ip_result.delete(0, tk.END)
            self.set_ip_result.insert(0, "IP格式错误")
            self.set_ip_result.config(state='readonly')
            return
        # 构造AA 00 06 00 97 85 ip1 ip2 ip3 ip4
        packet = bytearray([0xAA, 0x00, 0x06, 0x00, 0x97, 0x85])
        packet += bytes(ip_parts)
        hex_str = packet.hex(' ').upper()
        # 显示hex结果
        self.set_ip_result.config(state='normal')
        self.set_ip_result.delete(0, tk.END)
        self.set_ip_result.insert(0, hex_str)
        self.set_ip_result.config(state='readonly')
        # 发送串口
        port_info = self.port_combo.get()
        if not port_info:
            messagebox.showerror("错误", "请选择串口！")
            return
        port_name = port_info.split(" - ")[0]
        def worker():
            try:
                ser = serial.Serial(port_name, 9600, timeout=0.2)
                ser.write(packet)
                recv = ser.read(64)
                # 简单判断返回
                result = "设置成功" if recv else "无返回"
                self.safe_update_entry(self.set_ip_result, result)
                ser.close()
                # 设置成功后自动启动图片接收服务器（仅首次启动）
                if result == "设置成功":
                    import threading
                    def start_photo_server():
                        try:
                            main_photo_server()
                        except Exception as e:
                            print(f"[ERROR] 图片服务器启动失败: {e}")
                    threading.Thread(target=start_photo_server, daemon=True).start()
            except Exception as e:
                self.safe_update_entry(self.set_ip_result, f"串口错误: {e}")
        threading.Thread(target=worker, daemon=True).start()
    # ...existing code...
        self._clear_entry(self.set_wifi_rtsp_start)
        self.send_custom_hex('AA000300818501', self.set_wifi_rtsp_start, self.parse_wifi_rtsp_start)

    def send_set_wifi_rtsp_stop(self):
        self._clear_entry(self.set_wifi_rtsp_stop)
        self.send_custom_hex('AA000300898501', self.set_wifi_rtsp_stop, self.parse_wifi_rtsp_stop)

    def send_get_wifi_gw(self):
        self._clear_entry(self.get_wifi_gw)
        self.send_custom_hex('AA000300918501', self.get_wifi_gw, self.parse_wifi_gw)

    def _clear_entry(self, entry):
        entry.config(state='normal')
        entry.delete(0, tk.END)
        entry.config(state='readonly')

    def send_custom_hex(self, hex_str, result_entry, parse_func):
        port_info = self.port_combo.get()
        if not port_info:
            messagebox.showerror("错误", "请选择串口！")
            return
        port_name = port_info.split(" - ")[0]
        try:
            data = bytes.fromhex(hex_str)
        except ValueError:
            messagebox.showerror("错误", "指令格式错误！")
            return
        # 串口操作放到后台线程
        threading.Thread(target=self.serial_worker, args=(port_name, data, result_entry), daemon=True).start()

    def serial_worker(self, port_name, data, result_entry):
        try:
            try:
                ser = serial.Serial(port_name, 9600, timeout=0.2)
            except Exception as e:
                self.safe_showerror("串口打开失败", str(e))
                return
            try:
                ser.write(data)
                recv = ser.read(64)
                result = ""
                if recv:
                    # 按AA分帧
                    frames = []
                    buf = recv
                    while True:
                        idx = buf.find(b'\xAA')
                        if idx == -1:
                            break
                        # 找下一个AA作为帧尾
                        next_idx = buf.find(b'\xAA', idx+1)
                        if next_idx == -1:
                            frame = buf[idx:]
                            buf = b''
                        else:
                            frame = buf[idx:next_idx]
                            buf = buf[next_idx:]
                        if len(frame) >= 6:
                            frames.append(frame)
                        if not buf:
                            break
                    # IP专用分支
                    if result_entry == self.get_wifi_ip_result:
                        ip_results = []
                        for frame in frames:
                            parsed = self.parse_packet(frame)
                            if parsed:
                                ack = self.make_ack(parsed)
                                ser.write(ack)
                            parsed_result = self.auto_parse_recv(frame)
                            if parsed_result and parsed_result.startswith("IP:"):
                                if parsed_result not in ip_results:
                                    ip_results.append(parsed_result)
                        result = ' | '.join(ip_results) if ip_results else "无IP数据"
                    # 网关专用分支
                    elif result_entry == self.get_wifi_gw:
                        gw_results = []
                        for frame in frames:
                            parsed = self.parse_packet(frame)
                            if parsed:
                                ack = self.make_ack(parsed)
                                ser.write(ack)
                            parsed_result = self.auto_parse_recv(frame)
                            if parsed_result and parsed_result.startswith("GW:"):
                                if parsed_result not in gw_results:
                                    gw_results.append(parsed_result)
                        result = ' | '.join(gw_results) if gw_results else "无网关数据"
                    # 其它按钮分支
                    else:
                        results = []
                        for frame in frames:
                            parsed = self.parse_packet(frame)
                            if parsed:
                                ack = self.make_ack(parsed)
                                ser.write(ack)
                            parsed_result = self.auto_parse_recv(frame)
                            if parsed_result in ("开机成功", "关机成功", "EMMC格式化成功", "设置成功", "拍照成功") or parsed_result.startswith("版本号为 V"):
                                if parsed_result not in results:
                                    results.append(parsed_result)
                        result = ' | '.join(results) if results else self.auto_parse_recv(recv)
                self.safe_update_entry(result_entry, result)
            except Exception as e:
                self.safe_showerror("串口通讯失败", str(e))
            finally:
                ser.close()
        except Exception as e:
            self.safe_showerror("串口未知错误", str(e))

    def safe_update_entry(self, entry, value):
        # 主线程安全更新Entry内容
        if entry is None:
            return
        entry.after(0, lambda: self._update_entry(entry, value))

    def _update_entry(self, entry, value):
        if entry is None:
            return
        entry.config(state='normal')
        entry.delete(0, tk.END)
        entry.insert(0, value)
        entry.config(state='readonly')

    def safe_showerror(self, title, msg):
        # 主线程安全弹窗
        self.master.after(0, lambda: messagebox.showerror(title, msg))

    def auto_parse_recv(self, recv):
        if (
            len(recv) == 9 and
            recv[0] == 0xAA and
            recv[2] == 0x05 and recv[3] == 0x00 and
            recv[4] == 0x00 and recv[5] == 0x00 and recv[6] == 0xB3 and recv[7] == 0x85 and recv[8] == 0x00
        ):
            return "拍照成功"
        if (
            len(recv) == 9 and
            recv[0] == 0xAA and
            recv[2] == 0x05 and recv[3] == 0x00 and
            recv[4] == 0x00 and recv[5] == 0x00 and recv[6] == 0xAF and recv[7] == 0x85 and recv[8] == 0x00
        ):
            return "拍照成功"
        if (
            len(recv) == 9 and
            recv[0] == 0xAA and
            recv[2] == 0x05 and recv[3] == 0x00 and
            recv[4] == 0x00 and recv[5] == 0x00 and recv[6] == 0xB5 and recv[7] == 0x85 and recv[8] == 0x00
        ):
            return "设置成功"
        # 拍照成功 AA xx 05 00 00 00 B0 85 00
        if (
            len(recv) == 9 and
            recv[0] == 0xAA and
            recv[2] == 0x05 and recv[3] == 0x00 and
            recv[4] == 0x00 and recv[5] == 0x00 and recv[6] == 0xB0 and recv[7] == 0x85 and recv[8] == 0x00
        ):
            return "拍照成功"
        # 拍照成功 AA xx 05 00 00 00 B1 85 00
        if (
            len(recv) == 9 and
            recv[0] == 0xAA and
            recv[2] == 0x05 and recv[3] == 0x00 and
            recv[4] == 0x00 and recv[5] == 0x00 and recv[6] == 0xB1 and recv[7] == 0x85 and recv[8] == 0x00
        ):
            return "拍照成功"
        # 拍照成功 AA xx 05 00 00 00 B2 85 00
        if (
            len(recv) == 9 and
            recv[0] == 0xAA and
            recv[2] == 0x05 and recv[3] == 0x00 and
            recv[4] == 0x00 and recv[5] == 0x00 and recv[6] == 0xB2 and recv[7] == 0x85 and recv[8] == 0x00
        ):
            return "拍照成功"
        # 拍照成功 AA xx 05 00 00 00 B4 85 00
        if (
            len(recv) == 9 and
            recv[0] == 0xAA and
            recv[2] == 0x05 and recv[3] == 0x00 and
            recv[4] == 0x00 and recv[5] == 0x00 and recv[6] == 0xB4 and recv[7] == 0x85 and recv[8] == 0x00
        ):
            return "拍照成功"
        # 设置成功 AA xx 05 00 00 00 82 85 00
        if (
            len(recv) == 9 and
            recv[0] == 0xAA and
            recv[2] == 0x05 and recv[3] == 0x00 and
            recv[4] == 0x00 and recv[5] == 0x00 and recv[6] == 0x82 and recv[7] == 0x85 and recv[8] == 0x00
        ):
            return "设置成功"
        # 设置成功 AA xx 05 00 00 00 83 85 00
        if (
            len(recv) == 9 and
            recv[0] == 0xAA and
            recv[2] == 0x05 and recv[3] == 0x00 and
            recv[4] == 0x00 and recv[5] == 0x00 and recv[6] == 0x83 and recv[7] == 0x85 and recv[8] == 0x00
        ):
            return "设置成功"
        # 设置成功 AA xx 05 00 00 00 90 85 00（任意序号）
        if (
            len(recv) == 9 and
            recv[0] == 0xAA and
            recv[2] == 0x05 and recv[3] == 0x00 and
            recv[4] == 0x00 and recv[5] == 0x00 and recv[6] == 0x90 and recv[7] == 0x85 and recv[8] == 0x00
        ):
            print(f"[DEBUG] 识别为设置成功: {recv.hex(' ').upper()}")
            return "设置成功"
        # 关机成功 AA xx 05 00 00 00 87 85 00
        if (
            len(recv) == 9 and
            recv[0] == 0xAA and
            recv[2] == 0x05 and recv[3] == 0x00 and
            recv[4] == 0x00 and recv[5] == 0x00 and recv[6] == 0x87 and recv[7] == 0x85 and recv[8] == 0x00
        ):
            return "关机成功"
        # 关机成功 AA xx 02 00 87 85
        elif (
            len(recv) == 6 and
            recv[0] == 0xAA and
            recv[2] == 0x02 and recv[3] == 0x00 and
            recv[4] == 0x87 and recv[5] == 0x85
        ):
            return "关机成功"
        # 设置成功 AA xx 02 00 90 85（任意序号）
        if (
            len(recv) == 6 and
            recv[0] == 0xAA and
            recv[2] == 0x02 and recv[3] == 0x00 and
            recv[4] == 0x90 and recv[5] == 0x85
        ):
            print(f"[DEBUG] 识别为设置成功: {recv.hex(' ').upper()}")
            return "设置成功"

        if (
            len(recv) == 9 and
            recv[0] == 0xAA and
            recv[2] == 0x05 and recv[3] == 0x00 and
            recv[4] == 0x00 and recv[5] == 0x00 and recv[6] == 0x81 and recv[7] == 0x85 and recv[8] == 0x00
        ):
            print(f"[DEBUG] 识别为设置成功: {recv.hex(' ').upper()}")
            return "设置成功"
        # 设置成功 AA xx 02 00 81 85（任意序号）
        if (
            len(recv) == 6 and
            recv[0] == 0xAA and
            recv[2] == 0x02 and recv[3] == 0x00 and
            recv[4] == 0x81 and recv[5] == 0x85
        ):
            print(f"[DEBUG] 识别为设置成功: {recv.hex(' ').upper()}")
            return "设置成功"
        
        if (
            len(recv) == 9 and
            recv[0] == 0xAA and
            recv[2] == 0x05 and recv[3] == 0x00 and
            recv[4] == 0x00 and recv[5] == 0x00 and recv[6] == 0x89 and recv[7] == 0x85 and recv[8] == 0x00
        ):
            print(f"[DEBUG] 识别为设置成功: {recv.hex(' ').upper()}")
            return "设置成功"
        # 设置成功 AA xx 02 00 89 85（任意序号）
        if (
            len(recv) == 6 and
            recv[0] == 0xAA and
            recv[2] == 0x02 and recv[3] == 0x00 and
            recv[4] == 0x89 and recv[5] == 0x85
        ):
            print(f"[DEBUG] 识别为设置成功: {recv.hex(' ').upper()}")
            return "设置成功"
        # IP解析 AA xx 06 00 85 85 ip1 ip2 ip3 ip4
        if (
            len(recv) == 10 and
            recv[0] == 0xAA and
            recv[2] == 0x06 and recv[3] == 0x00 and
            recv[4] == 0x85 and recv[5] == 0x85
        ):
            ip_bytes = recv[6:10]
            ip_str = '.'.join(str(b) for b in ip_bytes)
            return f"IP: {ip_str}"
        # 网关解析 AA xx 06 00 91 85 gw1 gw2 gw3 gw4
        if (
            len(recv) == 10 and
            recv[0] == 0xAA and
            recv[2] == 0x06 and recv[3] == 0x00 and
            recv[4] == 0x91 and recv[5] == 0x85
        ):
            gw_bytes = recv[6:10]
            gw_str = '.'.join(str(b) for b in gw_bytes)
            print(f"[DEBUG] 网关协议识别: {gw_str} 原始: {recv.hex(' ').upper()}")
            return f"GW: {gw_str}"
        hexstr = recv.hex(' ').upper()
        # 设置成功 AA xx 05 00 00 00 E2 85 00
      
        if (
            len(recv) == 9 and
            recv[0] == 0xAA and
            recv[2] == 0x05 and recv[3] == 0x00 and
            recv[4] == 0x00 and recv[5] == 0x00 and recv[6] == 0x90 and recv[7] == 0x85 and recv[8] == 0x00
        ):
            return "设置成功"
        if (
            len(recv) == 9 and
            recv[0] == 0xAA and
            recv[2:4] == b'\x05\x00' and
            recv[4:7] == b'\x00\x00\xE2' and
            recv[7:9] == b'\x85\x00'
        ):
            return "设置成功"
        # 开机成功 AA xx 05 00 00 00 88 85 00
        if (
            len(recv) == 9 and
            recv[0] == 0xAA and
            recv[2:4] == b'\x05\x00' and
            recv[4:7] == b'\x00\x00\x88' and
            recv[7:9] == b'\x85\x00'
        ):
            return "开机成功"
        # EMMC格式化成功 AA xx 05 00 00 00 A0 85 00
        if (
            len(recv) == 9 and
            recv[0] == 0xAA and
            recv[2] == 0x05 and recv[3] == 0x00 and
            recv[4] == 0x00 and recv[5] == 0x00 and recv[6] == 0xA0 and
            recv[7] == 0x85 and recv[8] == 0x00
        ):
            return "EMMC格式化成功"
        # EMMC格式化成功 AA xx 02 00 A0 85
        if (
            len(recv) == 6 and
            recv[0] == 0xAA and
            recv[2] == 0x02 and recv[3] == 0x00 and
            recv[4] == 0xA0 and recv[5] == 0x85
        ):
            return "EMMC格式化成功"
        # 设置成功 AA F9 02 00 90 85
        if (
            len(recv) == 6 and
            recv[0] == 0xAA and
            recv[2] == 0x02 and recv[3] == 0x00 and
            recv[4] == 0x90 and recv[5] == 0x85
        ):
            return "设置成功"
        # 设置成功 AA 05 02 00 90 85
        if (
            len(recv) == 6 and
            recv[0] == 0xAA and
            recv[2] == 0x02 and recv[3] == 0x00 and
            recv[4] == 0x90 and recv[5] == 0x85
        ):
            return "设置成功"
        # 开机成功 AA xx 03 00 88 85 01
        if (
            len(recv) == 7 and
            recv[0] == 0xAA and
            recv[2:4] == b'\x03\x00' and
            recv[4:7] == b'\x88\x85\x01'
        ):
            return "开机成功"
        # 安卓/iOS设备 AA xx 03 00 E2 85 data
        if (
            len(recv) == 7 and
            recv[0] == 0xAA and
            recv[2:4] == b'\x03\x00' and
            recv[4:6] == b'\xE2\x85'
        ):
            data = recv[6]
            if data == 1:
                return "安卓设备"
            elif data == 3:
                return "iOS设备"
        # 版本号解析 AA xx 05 00 80 85 xx xx xx
        if (
            len(recv) == 9 and
            recv[0] == 0xAA and
            recv[2:4] == b'\x05\x00' and
            recv[4:6] == b'\x80\x85'
        ):
            v_bytes = recv[6:9]
            version = f"V{v_bytes[0]}.{v_bytes[1]}.{v_bytes[2]}"
            return f"版本号为 {version}"
        # 版本号解析 AA 1C 05 00 80 85 02 09 06（保留原有特殊情况）
        if len(recv) >= 9 and recv[0] == 0xAA and recv[1] in (0x1B, 0x1C) and recv[2:4] == b'\x05\x00':
            v_bytes = recv[-3:]
            version = f"v{v_bytes[0]}.{v_bytes[1]}.{v_bytes[2]}"
            return f"版本号为 {version}"
        # 其它情况显示原始数据
        return hexstr

    def parse_poweron(self, parsed, raw):
        # 可根据实际协议调整
        return "开机成功" if parsed else "失败"

    def parse_poweroff(self, parsed, raw):
        return "关机成功" if parsed else "失败"

    def parse_wifi_ip(self, parsed, raw):
        # 假设IP在payload里
        if parsed and parsed['payload']:
            try:
                ip = '.'.join(str(b) for b in parsed['payload'])
                return ip
            except:
                return "失败"
        return "失败"

    def parse_wifi_version(self, parsed, raw):
        # 假设版本号在payload里
        if parsed and parsed['payload']:
            try:
                v = parsed['payload'][0]
                return f"v{v}"
            except:
                return "版本解析失败"
        return "失败"

    def parse_wifi_ota(self, parsed, raw):
        return "OTA设置成功" if parsed else "失败"
    
    def parse_wifi_emmc_format(self, parsed, raw):
        return "EMMC格式化成功" if parsed else "失败"
    
    def parse_wifi_mode(self, parsed, raw):
        return "设置成功" if parsed else "失败"
    
    def parse_wifi_rtsp_start(self, parsed, raw):
        return "RTSP开启成功" if parsed else "失败"
    
    def parse_wifi_rtsp_stop(self, parsed, raw):
        return "RTSP停止成功" if parsed else "失败"
    
    def parse_wifi_gw(self, parsed, raw):
        # 假设网关在payload里
        print(f"[DEBUG] 网关数据")
        if parsed and parsed['payload']:
            try:
                gw = '.'.join(str(b) for b in parsed['payload'])
                print(f"[DEBUG] 网关解析: {gw} 原始: {raw.hex(' ').upper()}")
                return gw
            except Exception as e:
                print(f"[DEBUG] 网关解析失败: {e} 原始: {raw.hex(' ').upper()}")
                return "失败"
        print(f"[DEBUG] 未识别网关数据: {raw.hex(' ').upper()}")
        return "失败"

    def refresh_ports(self):
        ports = serial.tools.list_ports.comports()
        port_list = [port.device + " - " + port.description for port in ports]
        self.port_combo["values"] = port_list
        if port_list:
            self.port_combo.current(0)
        else:
            self.port_combo.set("")

    def send_hex(self):
        port_info = self.port_combo.get()
        if not port_info:
            messagebox.showerror("错误", "请选择串口！")
            return
        port_name = port_info.split(" - ")[0]
        hex_str = self.hex_entry.get().replace(' ', '')
        try:
            data = bytes.fromhex(hex_str)
        except ValueError:
            messagebox.showerror("错误", "请输入有效的十六进制字符串！")
            return
        try:
            with serial.Serial(port_name, 9600, timeout=1) as ser:
                ser.write(data)
                # 读取返回数据
                recv = ser.read(64)  # 最多读取64字节
                # 显示所有返回的十六进制
                self.hex_recv_entry.config(state='normal')
                self.hex_recv_entry.delete(0, tk.END)
                if recv:
                    self.hex_recv_entry.insert(0, recv.hex(' ').upper())
                else:
                    self.hex_recv_entry.insert(0, "无返回数据")
                self.hex_recv_entry.config(state='readonly')
                if recv:
                    parsed = self.parse_packet(recv)
                    if parsed:
                        # 自动回复ack
                        ack = self.make_ack(parsed)
                        ser.write(ack)
                        messagebox.showinfo("成功", f"数据已发送并自动回复ACK！\n收到: {recv.hex(' ')}")
                    else:
                        messagebox.showinfo("成功", f"数据已发送，收到: {recv.hex(' ')} (未识别数据包)")
                else:
                    messagebox.showinfo("成功", "数据已发送，无返回数据！")
        except Exception as e:
            messagebox.showerror("串口错误", str(e))

    def parse_packet(self, data):
        # 简单解析，按你举例的数据包格式
        if len(data) < 9:
            return None
        if data[0] != 0xAA:
            return None
        seq = data[1]
        length = int.from_bytes(data[2:4], 'big')
        if len(data) < 4 + length + 1:
            return None
        payload = data[4:4+length]
        value = data[4+length:4+length+2]
        last = data[4+length+2] if len(data) > 4+length+2 else None
        return {
            'seq': seq,
            'length': length,
            'payload': payload,
            'value': value,
            'last': last
        }

    def make_ack(self, parsed):
        # 构造ack包 AA 00 05 00 00 00 80 85 00
        ack = bytearray()
        ack.append(0xAA)
        ack.append(0x00)  # 序列号固定为00
        ack += (5).to_bytes(2, 'big')  # 长度5
        ack += b'\x00\x00'  # payload
        ack += b'\x80\x85'  # value
        ack.append(0x00)      # last
        return bytes(ack)

if __name__ == "__main__":
    root = tk.Tk()
    app = SerialSender(root)
    root.mainloop()
